package cs236369.webserver;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Scanner;

import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.impl.DefaultHttpServerConnection;
import org.apache.http.params.HttpParams;

class SocketReaderThread extends Thread {
	private final Queue<Socket> socketQueue;
	private final Queue<Request> requestQueue;
	private final HttpParams params;
	private final Config config;
	
	SocketReaderThread(Queue<Socket> _socketQueue,Queue<Request> _requestQueue,HttpParams _params, Config _config)
	{
		this.socketQueue = _socketQueue;
		this.requestQueue = _requestQueue;
		this.params = _params;
		this.config = _config;
	}
	public void run() {
		/*
		 *#.Dequeue the socketQueue if not empty,
		 *otherwise wait upon it.
		 *
		 *#.Extract the request from the socket.
		 *
		 *#.Enqueue the request queue if not full,
		 *  otherwise wait upon it. 
		 */
		while (true) {
			Socket socket = null;
			synchronized(socketQueue) {
				while (socketQueue.empty()) {
					try {
						socketQueue.wait();
					} catch (InterruptedException e) {
						Log.err("SocketReaderThread: socketQueue wait "+e.toString());
					}
				}
				socket = socketQueue.dequeue();
				Log.out("SocketReaderThread: Dequeued socket");
				socketQueue.notifyAll();	
			}
			//Now we have the socket in our hands and can handle it.
			DefaultHttpServerConnection conn = new DefaultHttpServerConnection();
			try {
				conn.bind(socket, params);
			} catch (IOException e) {
				Log.err("SocketReaderThread: bind "+e.toString());
			}
			String method = null;
			String uri = null;
			HttpRequest httpRequest = null; 
			try {
				httpRequest = conn.receiveRequestHeader();
			} catch (Exception e) {
				Log.err("SocketReaderThread: httprequest "+e.toString());
			}
			try {
				method = httpRequest.getRequestLine().getMethod();
				uri = httpRequest.getRequestLine().getUri();
			} catch (Exception e) {
				Log.err("SocketReaderThread: method & uri "+e.toString());
			}
			Request req = new Request(conn, httpRequest, config, getParams(method, uri, httpRequest));
			//Now we can populate the requestQueue.
			synchronized(requestQueue) {
				while (requestQueue.full()) {
					try {
						requestQueue.wait();
					} catch (InterruptedException e) {
						Log.err("SocketReaderThread: requestQueue wait "+e.toString());
					}
				}
				requestQueue.enqueue(req);
				Log.out("SocketReaderThread: Enqueued request");
				requestQueue.notifyAll();	
			}
		}
	}

	private HashMap<String,String> getParams(String method, String uri, HttpRequest httpRequest) {
		String query = null;
		if (method.equals("GET")) {
			if (uri.indexOf("?")==-1)
				return null;
			query = uri.substring(uri.indexOf("?")+1, uri.length());
		} else {
			HttpEntityEnclosingRequest ent = null;
			ent = (HttpEntityEnclosingRequest) httpRequest;
			StringBuilder sb = new StringBuilder();
			Scanner s = null;
			try {
				s = new Scanner(ent.getEntity().getContent());
			} catch (IllegalStateException e) {
				Log.err("SocketReaderThread: [getParams] scanner (IllegalStatementException) "+e.toString());
			} catch (IOException e) {
				Log.err("SocketReaderThread: [getParams] scanner (IOException) "+e.toString());
			}
			while (s.hasNext()) {
				sb.append(s.next());
			}
			query = sb.toString();
		}
		return parseParameters(query);
	}

	private HashMap<String,String> parseParameters(String query){
		HashMap<String,String> params = new HashMap<String, String>();
		String[] tup = query.split("&");
		for(String pair: tup){
			String[] array = pair.split("=");
			try {
				params.put(URLDecoder.decode(array[0], "UTF-8"), URLDecoder.decode(array[1], "UTF-8"));
			} catch (UnsupportedEncodingException e) {
				Log.err("SocketReaderThread: [parseParameters] parmas put (UnsupportedEncodingException) "+e.toString());
			}
		}
		return params;
	}
}
